function RemoveCallback(node)
    local n = node:getName();
    if g_Data.crashNode[node] then
        g_Data.crashNode[node] = nil;
    end
    
    node:removeFromParent()
    
    if node == g_Data.firstMonster then
        local v = g_Data.gate.talk[2];
        if v and #v>=2 then
            g_Data.skillAble = true;
            local function runFunc(postFunc)
                EnterTalk(g_Data.ui, v, postFunc);
            end
            PauseAndTalk(runFunc);
        end
    end

    if string.find(n,"monster") 
        and g_Data.role.chp>=0
        and #g_Data.monsterQueue<=0
        and not g_Data.gameover
    then
        local childs = g_Data.scene.layer:getChildren();
        for i=1,#childs do
            if string.find(childs[i]:getName(), "monster") then
                return false;
            end
        end
        print("end victory", #childs, n)
        g_Data.gameover = true;
        --if true then return false end;
        local action = cc.DelayTime:create(EXCEL.const["winSleepTime"]/1000)
        action = cc.Sequence:create(action, cc.CallFunc:create(GateVictory))
        g_Data.role:runAction(action)
    end
end

function GetNodeSize(node)
    local n = g_Data.crashNode[node];
    return n or node:getContentSize();
end

function checkIsBump(n1, n2, cv)
    if not n1 or not n2 then
        return false;
    end
    
    cv = cv or 1.0;
    cv = 1.0;   
    
    local x1,y1 = n1:getPosition();
    local a = n1:getAnchorPoint();
    local ax1,ay1 = a.x,a.y;
    local s1 = GetNodeSize(n1);
    local w1, h1 = s1.width * cv, s1.height * cv;
    
    local x2,y2 = n2:getPosition();
    local a = n2:getAnchorPoint();
    local ax2,ay2 = a.x,a.y;
    local s = n2:getContentSize();
    local s2 = GetNodeSize(n2);
    local w2, h2 = s2.width * cv, s2.height * cv;

    local c1 = g_Data.crashNode[n1];
    if c1 then
        local an = EXCEL.scale[c1.id]
        if an then
            w1 = w1 * an.scale;
            h1 = h1 * an.scale;
        end
    
        x1 = x1 + (c1.ox or c1.offsetX) - c1.offsetX;
        y1 = y1 + (c1.oy or c1.offsetY) - c1.offsetY;
        if c1.sx then
            w1 = c1.sx * cv * (an and an.scale or 1);
        end
        if c1.sy then
            h1 = c1.sy * cv * (an and an.scale or 1);
        end
    end
    
    local parent = nil;
    if n1:getParent() ~= g_Data.scene.layer then
        parent = n1:getParent();
    end    
    
    if parent then
        local rx,ry = parent:getPosition();
        local ra = parent:getAnchorPoint();
        local rs = parent:getContentSize()

        x1 = x1 - rs.width * ra.x + rx;
        y1 = y1 + rs.height * ra.y + ry;
    end
    
    local c2 = g_Data.crashNode[n2];
    if c2 then
        local an = EXCEL.scale[c2.id]
        if an then
            w2 = w2 * an.scale;
            h2 = h2 * an.scale;
        end
        
        x2 = x2 + (c2.ox or c2.offsetX) - c2.offsetX
        y2 = y2 + (c2.oy or c2.offsetY) - c2.offsetY
        if c2.sx then
            w2 = c2.sx * cv * (an and an.scale or 1);
        end
        if c2.sy then
            h2 = c2.sy * cv * (an and an.scale or 1);
        end
    end
    
    local xl1 = x1-ax1*w1;
    local xh1 = x1+ (1-ax1)*w1;
    local yl1 = y1-ay1*h1;
    local yh1 = y1+(1-ay1)*h1;

    local xl2 = x2-ax2*w2;
    local xh2 = x2 + ax2*w2;
    local yl2 = y2-ay2*h2;
    local yh2 = y2+(1-ay2)*h2;
    
    if ((xl1>=xl2 and xl1<=xh2) or (xl2>=xl1 and xl2<=xh1))
        and ((yl1>=yl2 and yl1<=yh2) or (yl2>=yl1 and yl2<=yh1)) then
        return true;
    end
    
    return false;
end

function AddNode(path, name, parent)
    if not string.find(path, ".png") then
        path = path.."/F1.png"
    end
    local node = nil;
    if parent then
        node = cc.Sprite:create(path);
        if node then
            cc.Sprite:create():setAnchorPoint(0.5,0.5)
            if name and name~="" then
                node:setName(name);
            end
            parent:addChild(node);
        end
    end
    return node;
end

function LoadArmature(armature, parent, nodeName, node, func, args, arr)
    if cc.FileUtils:getInstance():isFileExist(armature[1]) then
        local name = string.match(armature[1], "[[^.]*/]*([^/.]+)%.[^/.]+");
        if name then
            ccs.ArmatureDataManager:getInstance():addArmatureFileInfo(armature[1])
            arr = arr or ccs.Armature:create(name);

            arr:getAnimation():play(armature[2])
            if parent then
                parent:addChild(arr);
            end
            local crashNode = EXCEL.crashNode[name];
            g_Data.crashNode[arr] = crashNode;
            
            local s = arr:getContentSize();
            arr:setAnchorPoint(0.5+(crashNode and crashNode.offsetX or 0)/s.width
                ,0.5+(crashNode and crashNode.offsetY or 0)/s.height);
            if nodeName and nodeName~="" then
                arr:setName(nodeName);
            end
            
            if node then
                arr:setPosition(node:getPosition());
                RemoveCallback(node);
            end
--            local t = arr:boundingBox();
--            for k,v in pairs(t) do
--                if not string.find(armature[1], "monster") then
--                    print(name, armature[1], armature[2], k,v)
--                end
--            end
--            local t = arr:getShapeList()
--            
            
            if arr == g_Data.role then
                local x,y = arr:getPosition();
                local a = arr:getAnchorPoint();
            end
            
            if func then
                local function moveFunc(armatureBack,movementType,movementID)
                    local id = movementID
                    if 
                    ccs.MovementEventType.complete == movementType
                       -- or
                        -- ccs.MovementEventType.loopComplete == movementType
                    then
                        if func then
                            func(armatureBack, args);
                        end
                    end
                end
                arr:getAnimation():setMovementEventCallFunc(moveFunc)
            else
                local function moveFunc(armatureBack,movementType,movementID)
                    
                end
                arr:getAnimation():setMovementEventCallFunc(moveFunc)
            end

            arr:getAnimation():setSpeedScale(EXCEL.const["animationSpeedScale"])
            ccs.ArmatureDataManager:getInstance():removeArmatureFileInfo(armature[1])
            

            return arr;
        end
    end
end

function PlayArmature(arr, armature, func, args)
    LoadArmature(armature, nil, nil, nil, func, args, arr);  
end

function GetDist(x1, y1, x2, y2)
    return math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
end

function MoveTo(node, speed, dstX, dstY, func)
    node:stopActionByTag(GAME_TAG.ACTION_MOVE_TAG);
    local srcX,srcY = node:getPosition();
    dstX = dstX or srcX;
    dstY = dstY or srcY;
    local dist = GetDist(dstX, dstY, srcX, srcY);
    local action = cc.MoveTo:create(dist/speed, cc.p(dstX, dstY));
    if func then
        action = cc.Sequence:create(action, cc.CallFunc:create(func))
    end
    action:setTag(GAME_TAG.ACTION_MOVE_TAG);
    node:runAction(action)
    return dist/speed
end

function ShallowCopyTable(t)
    local nt = {};
    for k,v in pairs(t) do
        nt[k] = v;
    end
    return nt;
end

function AddTouchEventListener(root, name, func)
    if root then
        local child =root:getChildByName(name);
        if child then
            child:addTouchEventListener(func);
        end;
    end
end

function AddShade(parent)
    if parent then
        local shade = cc.LayerColor:create(cc.c4b(0,0,0,EXCEL.const["shadeDegree"]))
        parent:addChild(shade);
        return shade;
    end
    
    return nil;
end

function SetNodeEnable(node, isEnable, style)
    node:setEnabled(isEnable);
    if isEnable then
        node:setBrightStyle(ccui.BrightStyle.normal);
    else
        node:setBrightStyle(ccui.BrightStyle.highlight);
    end
end

function LoadAllNode(t, node)
    local childs = node:getChildren();
    for k,v in pairs(childs) do
        t[v:getName()] = v;
    end
end

function SetButtonFunc(node, func, name)
    node:addTouchEventListener(function(...) 
        if name and EXCEL.sound[name] then
            AudioEngine.playEffect(EXCEL.sound[name].value);
        else
            AudioEngine.playEffect(EXCEL.sound.clk.value);
        end
        func(...) end);
end

function TraverseNode(node, f)
    local enable = false;
    local childs = node:getChildren();
    
    if string.find(node:getName(), "uiAnimation") then
        return false;
    end
    
    for i=1, #childs do
        enable = TraverseNode(childs[i], f) or enable;
    end

    enable = enable or f(node);
    
    if node:getName()~="" then
        node:setEnabled(enable);
    end
    return enable;
end

function IsFloatEqual(a,b)
    local d = a-b;
    return d<CONST_FLOAT_DIFF and d>-CONST_FLOAT_DIFF;
end

function IsNewbie()
    local d = cc.UserDefault:getInstance();
    return not d:getBoolForKey("newbie") or EXCEL.const.testNewbie;
end

function SetTtf(node, ttf)
    if cc.FileUtils:getInstance():isFileExist(ttf) then
        node:setFontName(ttf)
    end
end

function GetUtf8(t)
    local v = "";
    
    if type(t)=="table" and #t>=2 and type(t[1])=="string" and type(t[2])=="string" then
        v = EXCEL[t[1]];
        v = v[t[2]]
        v = v and v[EXCEL.const.lang];
    end
    
    return v and v or "";
end

function BuildAttr(attr, curAttr)
    local desc = "";

    for k,v in ipairs(attr) do
        if v[2] > 0 then
            desc = desc .. GetUtf8(EXCEL.attr[v[1]].name) ..":"..v[2];
            if curAttr then
                local curV = 0;
                for k1,v1 in pairs(curAttr) do
                    if v1[1] == v[1] then
                        curV = v1[2];
                        break;
                    end
                end
                if curV < v[2] then
                    desc = desc .. " +" ..(v[2]-curV);
                end
            end
            desc = desc .. "\n";
        end
    end

    return desc; 
end

function CheckCrashCk(node, ck, interval)
    return (ck-g_Data.startCk)*1000 - (g_Data.crashCks[node] or -99999) >= interval;
end

function ReadConfigData(name, key, value)
    local v = cc.UserDefault:getInstance():
            getIntegerForKey(name.."."..(key or "nil"),value);
    if not key then
        g_Player[name] = v;
    else
        local t = g_Player[name] or {};
        g_Player[name] = t;
        t[key] = v;
    end
end

function GetConfigData(name, key, value)
    local v = 0;
    if not key then
        v = (g_Player[name] or 0);
    else
        local t = g_Player[name] or {};
        g_Player[name] = t;
        v = (t[key] or 0);
    end
    
    return v;
end

function SetConfigData(name, key, value)
    if not key then
        g_Player[name] = value;
    else
        local t = g_Player[name] or {};
        g_Player[name] = t;
        t[key] = value;
    end
    
    cc.UserDefault:getInstance():setIntegerForKey(name.."."..(key or "nil"),value);
    cc.UserDefault:getInstance():flush();
end

function AddConfigData(name, key, value)
    local v = 0;
    if not key then
        v = (g_Player[name] or 0);
    else
        local t = g_Player[name] or {};
        g_Player[name] = t;
        v = (t[key] or 0);
    end
    
    v = v + value;

    SetConfigData(name, key, v);
end

function ReduceConfigData(name, key, value)
    if not value or value<0 then
        return false;
    end
    
    local v = 0;
    if not key then
        v = (g_Player[name] or 0);
    else
        local t = g_Player[name] or {};
        g_Player[name] = t;
        v = (t[key] or 0);
    end

    if v<value then
        return false;
    end
    
    AddConfigData(name, key, -value);
    return true;
end

function GetItemData(key, value)
    SetConfigData("items", key,value);
end

function SetItemData(key, value)
    SetConfigData("items", key,value);
end

function AddItemData(key, value)
    AddConfigData("items", key, value)
end

function ReduceItemData(key, value)
    return ReduceConfigData("items", key, value);
end

function ReduceItemsData(items)
    for k,v in pairs(items) do
        if (g_Player.items[v[1]]) < v[2] then
            return false;
        end 
    end
    for k,v in pairs(items) do
        ReduceItemData(v[1], v[2])
    end
    
    return true;
end


